#include <os/timer.h>
#include <os/clock.h>
#include <sys/time.h>
#include <os/safety.h>
#include <os/walltime.h>
#include <os/safety.h>
#include <os/schedule.h>
#include <os/task.h>
#include <os/debug.h>
#include <sys/time.h>
#include <lib/time.h>
#include <lib/stddef.h>

int SysClockGetTime(clock_type_t clocktype, timespec_t *ts)
{
    timespec_t tmp_ts;

    if (!ts)
        return -1;

    switch (clocktype)
    {
    case CLOCK_REALTIME:
        tmp_ts.tv_sec = WallTimeMakeTimeStamp(&walltime);
        tmp_ts.tv_nsec = (do_div(sys_ticks, HZ) * MS_PER_TICKS) * 1000000;
        break;
    case CLOCK_BOOTTIME:
        tmp_ts.tv_sec = do_div64(sys_ticks, HZ);
        tmp_ts.tv_nsec = (do_div(sys_ticks, HZ) * MS_PER_TICKS) * 1000000;
        break;
    case CLOCK_PROCCESS_CPUTIME:
        tmp_ts.tv_sec = do_div64((cur_task->elapsed_ticks), HZ);
        tmp_ts.tv_nsec = (do_div(cur_task->elapsed_ticks, HZ) * MS_PER_TICKS) * 1000000;
        break;
    case CLOCK_THREAD_CPUTIME:
        tmp_ts.tv_sec = do_div64((cur_task->elapsed_ticks), HZ);
        tmp_ts.tv_nsec = (do_div(cur_task->elapsed_ticks, HZ) * MS_PER_TICKS) * 1000000;
        break;
    default:
        return -1;
        break;
    }
    return MemCopyToUser(ts, &tmp_ts, sizeof(timespec_t));
}

uint64_t TimevalToSysticks(timeval_t *tv)
{
    uint64_t sec = tv->tv_sec;
    uint64_t usec = tv->tv_usec;

    if (sec >= do_div64(SYSTICKS_VALUE_MAX, HZ))
    {
        return SYSTICKS_VALUE_MAX;
    }
    usec = do_div64(usec, do_div64(1000000L, HZ));
    return HZ * sec + usec;
}

uint64_t TimespecToSysticks(timespec_t *ts)
{
    uint64_t sec = ts->tv_sec;
    uint64_t nsec = ts->tv_nsec;

    if (sec >= do_div64(SYSTICKS_VALUE_MAX, HZ))
    {
        return SYSTICKS_VALUE_MAX;
    }
    nsec = do_div64(nsec, do_div64(1000000000, HZ));

    return HZ * sec + nsec;
}

void SysticksToTimeval(clock_t ticks, timeval_t *tv)
{
    uint64_t sec = do_div64(ticks, HZ);
    uint64_t usec = do_div(ticks, HZ) * MS_PER_TICKS;

    if (sec >= (SYSTICKS_VALUE_MAX / HZ))
    {
        sec = SYSTICKS_VALUE_MAX / HZ;
    }
    tv->tv_sec = sec;
    tv->tv_usec = usec;
}

clock_t SysTime(tms_t *tms)
{
    task_t *cur=NULL;
    task_t *child=NULL;

    if (tms)
    {
        cur = cur_task;

        tms->time_sys_time = cur->syscall_ticks;
        tms->time_user_time = cur->elapsed_ticks;

        // sum child process time
        list_traversal_all_owner_to_next(child, &task_global_list, list)
        {
            if (child->parent_pid == cur->pid)
            {
                tms->time_cs_time += child->syscall_ticks;
                tms->time_cu_time += child->elapsed_ticks;
            }
        }
    }
    return SysGetTicks();
}

int SysGetTimeOfDay(timeval_t *tv, timezone_t *tz)
{
    timeval_t temp_tv;
    timezone_t temp_tz;
    if (tv)
    {
        temp_tv.tv_sec = WallTimeMakeTimeStamp(&walltime);
        temp_tv.tv_usec = (do_div(sys_ticks, HZ) * MS_PER_TICKS) * 1000;
        if (MemCopyToUser(tv, &temp_tv, sizeof(timeval_t)))
            return -1;
    }
    if (tz)
    {
        temp_tz.tz_dsttime = 0;
        temp_tz.tz_minutewest = 0;
        if (MemCopyToUser(tz, &temp_tz, sizeof(timezone_t)))
            return -1;
    }
    return 0;
}